package main

import (
	"go/ast"
	"go/parser"
	"go/token"
	"io/ioutil"
)

type structInfo struct {
	name string
	types []string
}

func contains(arr []string, tar string) bool {
	for _, s := range arr {
		if s == tar{
			return true
		}
	}
	return false
}

func getCodeInfo(filename string) (string,map[string][]string, []*structInfo) {

	// src is the input for which we want to print the AST.
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	src := string(data)
	// Create the AST by parsing src.
	fset := token.NewFileSet() // positions are relative to fset
	f, err := parser.ParseFile(fset, filename, src, 0)
	if err != nil {
		panic(err)
	}

	var mapper = make(map[string][]string)
	var structName string
	var typeName string
	for _, d := range f.Decls {
		if genDecl, ok := d.(*ast.GenDecl); ok {
			for _, space := range genDecl.Specs {
				if typeSpec, ok := space.( *ast.TypeSpec); ok {
					structName = typeSpec.Name.Name
					mapper[structName] = nil
					if structType, ok := typeSpec.Type.(*ast.StructType); ok {
						for _, field := range structType.Fields.List {
							if field.Names == nil {
								if starExpr, ok :=field.Type.( *ast.StarExpr ); ok {
									if ident, ok := starExpr.X.(*ast.Ident); ok {
										typeName = ident.Name
										mapper[structName] = append(mapper[structName], typeName)
									}
								}
							}
						}
					}
				}
			}
		}
	}


	var getAll func(cur string) []string
	getAll = func(name string) []string {
		var result []string
		for _, t := range mapper[name] {
			result = append(result, t)
			for _, s := range getAll(t) {
				if !contains(result, s) {
					result = append(result, s)
				}
			}
		}
		return result
	}

	//ast.Print(fset, f)
	var result []*structInfo
	for s, _ := range mapper {
		result = append(result, &structInfo{
			name:  s,
			types: getAll(s),
		})
	}

	return f.Name.Name, mapper, result
}
